<!DOCTYPE html>
<html lang="en">
    <head>
	<meta charset="UTF-8">
	<title>计算报价(复式运算)</title>
	<style type="text/css">
	    #barChart {
		position: absolute;
		/*width: 400px;*/
		height: 200px;
		right: 10px;
		top: 10px;
	    }
	    /*柱状图样式*/
	    .chart rect {
		stroke: white;
		fill: steelblue;
	    }
	    .chart rect.gray {
		fill: gray;
	    }

	    .chart rect:hover {
		fill: brown;
	    }
	    #main input{
		width: 1.5em;
		text-align: right;
	    }
	    /*控制坐标轴样式*/
	    .axis path,
	    .axis line {
		fill: none;
		stroke: black;
		shape-rendering: crispEdges;
	    }

	    .axis text {
		font-family: sans-serif;
		font-size: 11px;
	    }

	    .axis g text {
		font-family: sans-serif;
		font-size: 11px;
		color: black;
	    }

	    .axis.red {
		color: red;
	    }
	    .red {
		color: red;
	    }
	    .green {
		color: green;
	    }
	    input#otherLineHigh, input#otherLineLow{
		width: 2em;
	    }
	</style>
	<script src="jquery.js"></script>
	<script src="underscore-min.js"></script>
	<script type="text/javascript">
	    var stdout = function(x) {
	        console.log(x);
	        $("#ans").append("<br>" + x);
	    }
	    $(document).ready(function() {
	        $("form").submit(function() {
	            event.preventDefault();
	        });
	        $("#deal").click(dealAndShow);
	        $("#animi").click(function() {
	            curTestData = parseFloat($("#otherLineHigh").val()) || curTestData;
	            curTestDataLower = parseFloat($("#otherLineLow").val()) || curTestDataLower;
	            ourData = $("textarea").val().split("\n").map(function(x, i) {
	                return parseFloat(x) || 0;
	            });
	            showAnima();
	        });
	    });
	    var curTestData = 600;
	    var curTestDataLower = 400;
	    var allAnimiAns = [];
	    var ourData = [535, 546, 521, 543];

	    var datas = [];
	    var orgdatas = [];
	    var points = [];
	    var matchData = []; //在A1 区间 -20%~ 10%以内的
	    var notMatchData = [];
	    var A, A1;
	    var highLine, lowLine, mLineHigh, mLineLow, nLine, nWeight;

	    function showAnima() {
	        setTimeout(function() {
	            var ourD = ourData.slice(0);
	            ourD.push(curTestData);
	            if (curTestData >= curTestDataLower) {
	                dealAndShow({
	                    data: ourD
	                });
	                curTestData -= 1;
	                showAnima();
	            } else {
	                $("#ans").html(allAnimiAns.join('<br>'));
	            }
	        }, 60);
	    }

	    function dealAndShow(ds) {
	        dealDatas(ds);

	        D3Data = datas.map(function(x, i) {
	            var pointToShow = (points[i] == 0) ? -i : points[i];
	            return {
	                Downtime: x,
	                EquipName: pointToShow,
	                index: i
	            };
	        });
	        //BarChart(D3Data.sort(function(a,b){return a.EquipName - b.EquipName}));
	        BarChart(D3Data.sort(function(a, b) {
	            return a.EquipName - b.EquipName
	        }));
	        //event.preventDefault();
	    }

	    function calcDatas__0() { //原始数据确认.
	        //判定个数是否在[20,30)区间,在的话去除两个最高分一个最低分再计算
	        A4 = average(datas);
	        if (datas.length < 1000) {
	            //alert("当前配置下,数据量小于10,请确认数据量");
	            //return;
	        } else if (datas.length >= 10 && datas.length < 20) {
	            alert("当前配置下,数据量大于等于10小于20,请确认数据量");
	            //return;
	        } else {
	            alert("当前配置下,数据量大于20,请确认数据量. 暂不区分20以上和30以上");
	            return;
	        }
	        orgdatas = datas.slice(0);
	        var sortDatas = datas.sort(function(a,b) {
	        	return a-b;
	        });
	        var tmpMin = sortDatas[0];
	        if(true){
	        	datas = sortDatas.slice(0); //不限定数量
	        }
	        else if (datas.length < 10) {
	            datas = sortDatas.slice(0);
	        } else {
	            datas = sortDatas.slice(1);
	            var tmpMax0 = datas.pop();
	            if (datas.length >= 20) {
	                var tmpMax1 = datas.pop();
	                stdout('数据 ' + tmpMax0 + " " + tmpMax1 + " " + tmpMin + ' 为两个最高分和一个最低分,已经在后续计算中去除');
	            } else {
	                stdout('数据 ' + tmpMax0 + " " + tmpMin + ' 为一个最高分和一个最低分,已经在后续计算中去除');
	            }
	        }

	        A = calc(datas);
	        if (A == -1) {
	            return;
	        }
	        var ans = calcAns(orgdatas, A);
	        points = ans;
	        return ans;
	    }
	    function dealDatas(ds) {
	        lowLine = 1 - parseFloat($("#lowerLine").val()) / 100;
	        highLine = 1 + parseFloat($("#higherLine").val()) / 100;
	        nLine = parseFloat($("#nLine").val()) || 1;
	        mLineHigh = parseFloat($("#mLineHigh").val()) || 1;
	        mLineLow = parseFloat($("#mLineLow").val()) || 0.3;
	        nWeight =  parseFloat($("#nWeight").val()) || 1;

	        $("#ans").text($("textarea").val());
	        if (ds && ds.data) {
	            datas = ds.data;
	        } else {
	            datas = _.map($("textarea").val().split("\n"), function(x, i) {
	                return parseFloat(x) || 0;
	            });
	        }
	        for (var i = 0; i < datas.length; i++) {
	            if (datas[i] == 0) {
	                alert("数据里面有空行,请检查后重试");
	                return;
	            }
	        }
	        var ans = calcDatas__0();
	        datas = orgdatas;
	        var anstext = [];
	        var sortresult = [];
	        for (var i = 0; i < ans.length; i++) {
	            anstext.push(datas[i] + "  :  " + ans[i]);
	            sortresult.push([datas[i], ans[i]]);
	        }
	        //allAnimiAns.push(sortresult.sort(function(a,b){return a[1]> b[1];}).map(function(x){
	        allAnimiAns.push(_.map(_.sortBy(sortresult, function(a) {
	            return a[1];
	        }), function(x) {
	            var classN = _.contains(ourData, x[0]) ? 'class="green"' : 'class="red"';
	            return '<span ' + classN + '>' + x[0] + '</span>';
	        }).join(', '));
	        $("#ans").append('<br>' + ans.join("  ") + '<hr>灰色是废标方,蓝色为入围方,从左向右按分数升序排列<br>最终分数<br/>' + anstext.join("<br/>"));
	    }

	    function calcAns(data, A) {
	        return data.map(function(x, i) {
	        	//if(_.contains(notMatchData,x)){
	        	//	return 0;
	        	//}
	            if (x >= A) {
	                m = mLineHigh; //评标价格高于或等于基准价格
	            } else {
	                m = mLineLow; //评标价格低于基准价格
	            }
	            var n = nLine;
	            /*
			    //超出区间直接取分数为0分
			    if(x > A*highLine || x<A*lowLine){
			    return 0;
			    }*/
	            var res = 100 - 100 * m * n * Math.abs(x - A) / A;
	            res = res < 0 ? 0 : res;
	            res = res * nWeight; //乘以权重
	            return Math.floor(res * 1000) / 1000; //精确到小数点后面三位
	        });
	    }

	    function calc(data) { //实际数据计算
	        var leng = data.length;
	        stdout(data);
	        A1 = average(data);
	        var N = data.length;
	        //最高价448.5
	        stdout("平均值A1 (N个单位): " + A1 + " (" + N + "个单位)");
	        stdout("取值区间:" + A1 * lowLine + "  " + A1 * highLine);
	        matchData = []; //在A1 区间 -20%~ 10%以内的
	        notMatchData = [];
	        for (var i = 0; i < data.length; i++) {
	            var curData = data[i];
	            if (isMatch(curData, A1)) {
	                matchData.push(curData);
	            } else {
	                notMatchData.push(curData);
	            }
	        }

	        var A2 = average(matchData);
	        var P = matchData.length;
	        stdout("平均值A2 : " + A2 + " (保留" + P + "个竞标厂家)");
	        stdout("被保留的值: " + matchData.join(" "));

	        if (P == 0) {
	            stdout("合适区间内无数据,平均值A与A4相同: " + A4);
	            stdout("【最终基准价】为: " + A4*0.9);
	            return A4*0.9;
	        }
	        stdout("【最终基准价】为: " + A2*0.9);
	        return A2*0.9; //单次平均
	        
	        /** 次平均复式
	        var A3 = (A2 + listMin(matchData)) / 2;
	        stdout("平均值A3: " + A3);
	        return A3;
	        **/
	    }

	    /** 判断价格是否在合理竞价区间内 **/
	    /* 区间 -0.2<=x<= +0.1 **/
	    /* 区间 -0.20<=x<= +0.10 */
	    function isMatch(value, average) {
	        return (value >= average * lowLine) && (value <= average * highLine);
	    }


	    /** 求数组平均值 **/
	    function average(data) {
	        if (data.length == 0) {
	            return -1;
	        }
	        var sum = 0;
	        for (var i = 0; i < data.length; i++) {
	            sum += data[i];
	        }
	        return sum / data.length;
	    }
	    /** 求最大值 **/
	    function listMax(data) {
	        var max = data[0];
	        for (var i = 1; i < data.length; i++) {
	            if (data[i] > max) {
	                max = data[i];
	            }
	        }
	        return max;
	    }
	    /** 求最小值 **/
	    function listMin(data) {
	        var min = data[0];
	        for (var i = 1; i < data.length; i++) {
	            if (data[i] < min) {
	                min = data[i];
	            }
	        }
	        return min;
	    }
	    /** 创建随机数组 **/
	    function creatRandomList(size, maxNum, minNum) {
	        minNum = minNum || 0;
	        maxNum = (maxNum || 100) - minNum;
	        var ans = [];
	        for (var i = 0; i < size; i++) {
	            ans.push(Math.floor(Math.random() * maxNum) + minNum);
	        }
	        return ans;
	    }
	</script>
	<script type="text/javascript" src="d3/d3.min.js"></script>
	<script type="text/javascript">
	    //柱状图表js,我下面的东西都是一块一块的，用上哪段就能得到想要的效果
	var D3Data = [
	    {EquipName:'ssss',Downtime:1},
	    {EquipName:'ssscs',Downtime:3},
	    {EquipName:'sssxxs',Downtime:4},
	    {EquipName:'ssxxxss',Downtime:2},
	    {EquipName:'xxssss',Downtime:8},
	    {EquipName:'ssssff',Downtime:2},
	    {EquipName:'ssasss',Downtime:3},
	    {EquipName:'ssssss',Downtime:6},
	];
	function BarChart(data) {
	    //?d3.scale.ordinal()和他们的domain,,,translate，dy属性

	    // 1、清空图表区内的图表,重新生成
	    $('#barChart').html("");

	    // 2、定义初始化数据，图表的上下左右距离和宽高,连续定义
	    var margin = { top: 60, right: 30, bottom: 70, left: 50 },
	    width = 600 - margin.left - margin.right,
	    height = 400 - margin.top - margin.bottom;

	    // 3、创建容器并移动位置到合适(注意：这点因为append了一个g所以返回的svg其实是这个g，后面的元素都在这里，定位也是相对于他)
	    svg = d3.select("div#barChart").append("svg")
	    .attr("class", "chart")
	    .attr("width", width + margin.left + margin.right)
	    .attr("height", height + margin.top + margin.bottom)
	    .append("g")
	    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

	    // 4、定义计算X轴的线性比例尺的方法
	    //散列值 把width平均分配到data的每个数据段（比如是6个） 0~80，80~160,...值为（0，80，160，...）域宽80
	    var xScale = d3.scale.ordinal()
	    .domain(data.map(function(d) { return d.EquipName; }))
	    //.rangeRoundBands([0,width], .1,1);//两种效果不一样
	    .rangeRoundBands([0, width], 0.1);

	    // 5、定义计算Y轴的比例尺的方法
	    var yScale = d3.scale.linear()
	    .domain([0, d3.max(data, function(d) { return d.Downtime; })])
	    .range([height, 0]);

	    // 6、创建X坐标轴函数
	    var xAxis = d3.svg.axis()
	    .scale(xScale)
	    .orient("bottom");

	    // 7、创建Y轴坐标轴函数
	    var yAxis = d3.svg.axis()
	    .scale(yScale)
	    .orient("left");

	    // 8、创建柱状图（注意：貌似定位都是相对于左上角）
	    svg.selectAll(".barRect").data(data).enter().append("rect")
	    .attr("class", "barRect")
	    .attr("x", function(d) { return xScale(d.EquipName); })//相当于function(d){return xScale(d);}
	    //.attr("x", function(d, i) {return i * (width / data.length);})
	    .attr("y", function(d) { return yScale(d.Downtime); })//svg的坐标以左上角为原点,通过高度运算转成原点在左下角的效果
	    .attr("width", xScale.rangeBand()) //获取散列值每段的长度 为矩形的宽
	    //.attr("width", width / data.length - 10) //获取散列值每段的长度 为矩形的宽
	    .attr("height", function(d) { return height - yScale(d.Downtime); })// 通过函数yScale  function(d){return  (420/42)*d}  得到矩形的高
	    .attr("class",function(d){
		return (d.Downtime > A1*highLine || d.Downtime < A1*lowLine)?"barRect gray":"barRect";
	    }); 

	    // 9、加背景横线
	    svg.selectAll("line")
	    .data(yScale.ticks(10))   //y.ticks 根据权重 把数据进行划分层次，增加可读性。可以自己改变ticks的值察看效果来理解
	    .enter().append("line")   //画线 （x1,y1） ------> (x2,y2)
	    .attr("x1", 0)
	    .attr("x2", width)
	    .attr("y1", function(d) { return height - yScale(d); })
	    .attr("y2", function(d) { return height - yScale(d); })
	    .style("stroke", "#ccc");



	    // 10、加图表标题
	    svg.append("text")
	    .attr("x", (width / 2))
	    .attr("y", -40)
	    .attr("text-anchor", "middle")
	    .style("font-size", "16px")
	    .style("text-decoration", "underline")
	    .text("报价比较趋势图");

	    // 11、添加矩形上方的数字
	    svg.selectAll(".label")
	    .data(data)
	    .enter().append("text")
	    .attr("class", "label")
	    .attr("x", function(d) { return xScale(d.EquipName) + xScale.rangeBand() / 2; })  //散列值+散列宽度的一半
	    .attr("y", function(d) { return yScale(d.Downtime); })
	    .attr("dx", ".35em") //  horizontal-align: middle 居中对齐
	    .attr("dy", 0) // vertical-align: middle //垂直方向无偏移
	    .attr("text-anchor", "end") // text-align: right
	    .text(function(d) { return d.Downtime; });
	    //.text(String); //设置数据为显示值 相当于.text(function(d){ return d;}


	    // 最后加坐标轴否则影响很多东西
	    // 99、添加X坐标轴及坐标轴名称
	    svg.append("g")
	    .attr("class", "axis")
	    .attr("transform", "translate(0," + height + ")")
	    .call(xAxis)
	    .append("text")
	    .attr("transform", "translate(" + (width) + "," + 20 + ")")
	    .style("text-anchor", "middle")
	    .text("得分(升序排列)");

	    // 如果x轴名称太长将其倾斜
	    svg.selectAll(".axis g text")
	    .style("text-anchor", "end")
	    .attr("dx", "-.8em")
	    .attr("dy", ".15em")
	    .attr("transform", "rotate(-35)");

	    // 100、添加Y坐标轴及坐标轴名称
	    svg.append("g")
	    .attr("class", "axis")
	    .call(yAxis)
	    .append("text")
	    .attr("transform", "rotate(0)")
	    .attr("x", 50)
	    .attr("y", -20)
	    .attr("dy", ".71em")
	    .style("text-anchor", "end")
	    .text("报价");


	    svg.append("g")
	    .attr("class", "axis red")
	    .call(yAxis)
	    .append("text")
	    .attr("transform", "rotate(0)")
	    .attr("x", 0)
	    .attr("y",   yScale(A))
	    .attr("dy", ".71em")
	    //.style("text-anchor", "end")
	    .text("基准价");

	    svg.append("g")
	    .attr("class", "axis red")
	    .call(yAxis)
	    .append("text")
	    .attr("transform", "rotate(0)")
	    .attr("x", 0)
	    .attr("y",   yScale(A1*lowLine))
	    .attr("dy", ".71em")
	    //.style("text-anchor", "end")
	    .text("下限")
	    .style("color","red");

	    svg.append("g")
	    .attr("class", "axis red")
	    .call(yAxis)
	    .append("text")
	    .style("color","red")
	    .attr("transform", "rotate(0)")
	    .attr("x", 0)
	    .attr("y",   yScale(A1*highLine))
	    .attr("dy", ".71em")
	    //.style("text-anchor", "end")
	    .text("上限");

	    svg.selectAll("line .redline")
	    .data([A1*lowLine, A, A1*highLine])   
	    .enter().append("line") 
	    .attr("x1", 0)
	    .attr("x2", width)
	    .attr("y1", function(d) { return  yScale(d); })
	    .attr("y2", function(d) { return  yScale(d); })
	    .style("stroke", "red"); 
	}
    </script>
    <script>
	var foo = function( value ){
	console.log( 'foo:' + value.x + "  " + value.y );
	//改成自己的实际代码
	}
	var controlCenter = $.Callbacks();
	controlCenter.add( foo );

	window.onresize= function(){
	var winH = $(window).height();
	var footH = 100; //todo:等待改为实际代码
	controlCenter.fire({x:winH, y:footH});
	}

    </script>
</head>
<body>
    <h2>竞价报价预估算工具</h2>
    <div id="main">
	<form>
	    <textarea rows="10" cols="20 id="datas">200
220
300
150
230
210</textarea>
	    <br />
	    <button type="button" id="deal">计算</button> <span>&nbsp;下限-</span><input id="lowerLine" value=20 />% <span>&nbsp;上限+</span><input id="higherLine" value=20 />%
	    <span style="display:none;"> &nbsp;n</span><input  style="display:none;" id="nLine" value=1 />
	    <span> &nbsp;m竞标价高于基准价时 1</span> &nbsp;m竞标价低于基准价时</span><input id="mLineLow" value=0.5 /> &nbsp;n取值:<input id="mLineHigh" value=1>
	    <span style="display:none;"> &nbsp;权重</span><input  style="display:none;" id="nWeight" value=1 /><hr/>
	<button type="button" id="animi">N控1</button> <span> &nbsp;对方上限</span><input id="otherLineHigh" value=195 /> &nbsp;对方下限</span><input id="otherLineLow"  value=120 />
    <br />
    <div id="ans">&nbsp;【区间平均价法】：(不按照数量进行最高报价最低报价过滤)<br/>
    M：为进入详评的投标人数量<br/>
计算M个进入详评的投标人评标价的算术平均值A1；<br/>
再剔除（剔除只为计算平均值使用，并不是废标）评标价与算术平均值A1 偏差超过[0.8，1.2]<br/>区间的投标人报价，计算偏差在算术平均值A1 [0.8，1.2]<br/>区间以内的投标人评标价的算术平均值A2，以A2*0.9为基准价；<br/>
若M个评标价均在算术平均值A1 [0.8，1.2]<br/>区间以外，则所有进入详评的投标人评标价的算术平均值A1*0.9作为基准价。<br/>

    价格得分=（100-100 × n × m ×｜投标人评标价-评标基准价｜/评标基准价）×权重<br/>
    * 投标人评标价≥评标基准价时，系数 1<br/>
	* 投标人评标价≤评标基准价时，系数 0.5<br/>

    当计算出的价格部分得分<0，按0分计。
    </div>
</form>
<div id="barChart"></div>
	</div>
    </body>
</html>
